package allen.log;

import java.sql.Date;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.swing.table.AbstractTableModel;

/**
 * Hold all the log entities in the memory. And it is the log entities model for
 * JTable.
 * */
public class LogDataCenter extends AbstractTableModel {

	private static final long serialVersionUID = 1L;

	public static final LogLevel Default_Min_LogLevel = LogLevel.Debug;
	public static final int Default_Max_Capacity = 100;
	/**
	 * Indicator of log data center's on or off.
	 * */
	public static final boolean Default_Switch = true;

	public static LogDataCenter logDataCenter = new LogDataCenter();

	private List<LogEntity> logEntities = Collections
			.synchronizedList(new ArrayList<LogEntity>());
	private LogLevel minLogLevel;
	private int maxCapacity;
	private boolean logOnSwitch;

	private LogDataCenter() {
		useDefaultSetting();
	}

	/**
	 * use default setting.
	 * */
	void useDefaultSetting() {
		logOnSwitch = Default_Switch;
		setMinLogLevel(Default_Min_LogLevel);
		setMaxCapacity(Default_Max_Capacity);
	}

	/**
	 * Add log entity to the memory when the log entity's log level is not below
	 * the min log level. Remove one oldest log entity if there is no place to
	 * add new log entity.
	 * */
	void addLogEntity(LogEntity logEntity) {
		if (logEntity == null || logOnSwitch == false)
			return;

		if (!logEntity.getLevel().isBelow(minLogLevel)) {
			if (logEntities.size() >= maxCapacity) {
				logEntities.remove(maxCapacity - 1);
				fireTableRowsDeleted(maxCapacity - 1, maxCapacity - 1);
			}
			logEntities.add(0, logEntity);
			fireTableRowsInserted(0, 0);
		}
	}

	/**
	 * Is log on.
	 * */
	public boolean isLogOn() {
		return logOnSwitch;
	}

	/**
	 * Get min log level.
	 * */
	public LogLevel getMinLogLevel() {
		return minLogLevel;
	}

	/**
	 * Get max capacity.
	 * */
	public int getMaxCapacity() {
		return this.maxCapacity;
	}

	/**
	 * Turn on or turn off log.
	 * */
	public void turnLog() {
		this.logOnSwitch = !this.logOnSwitch;
	}

	/**
	 * Clear all log entities.
	 * */
	public void clearAllLogEntities() {
		this.logEntities.clear();
		fireTableDataChanged();
	}

	/**
	 * Set min log level, all the log entities below the log level in the memory
	 * will be removed.
	 * */
	public void setMinLogLevel(LogLevel logLevel) {
		if (logLevel == null)
			return;
		this.minLogLevel = logLevel;
		boolean isRemoved = false;
		synchronized (logEntities) {
			for (Iterator<LogEntity> iterator = logEntities.iterator(); iterator
					.hasNext();) {
				if (iterator.next().getLevel().isBelow(minLogLevel)) {
					iterator.remove();
					isRemoved = true;
				}
			}
		}
		if (isRemoved) {
			fireTableDataChanged();
		}
	}

	/**
	 * Set max capacity, this is the max capacity of log entities in the memory,
	 * if the log entities in the memory exceeds the max value, remove the older
	 * log entity.
	 * 
	 * @param capacity
	 *            greater than 0.
	 * */
	public void setMaxCapacity(int capacity) {
		if (capacity <= 0)
			return;
		this.maxCapacity = capacity;

		if (this.logEntities.size() > maxCapacity) {
			final int exceedCount = this.logEntities.size() - maxCapacity;
			int count = exceedCount;
			while (count-- > 0) {
				logEntities.remove(maxCapacity);
			}
			fireTableRowsDeleted(maxCapacity, maxCapacity - 1 + exceedCount);
		}
	}

	/**
	 * Get all the log entities's names.
	 * */
	public String[] getLogEntityNames() {
		Set<String> names = new HashSet<String>();
		for (LogEntity le : logEntities) {
			names.add(le.getLogName());
		}

		return names.toArray(new String[0]);
	}

	/**
	 * Get log entity of row.
	 * */
	public LogEntity getLogEntity(int row) {
		return logEntities.get(row);
	}

	@Override
	public String getColumnName(int column) {
		switch (column) {
		case 0:
			return "Name";
		case 1:
			return "Level";
		case 2:
			return "Date";
		case 3:
			return "Message";
		case 4:
			return "Throwable";
		default:
			return "";
		}
	}

	@Override
	public int getColumnCount() {
		return 5;
	}

	@Override
	public int getRowCount() {
		return logEntities.size();
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		if (rowIndex >= getRowCount() || columnIndex >= getColumnCount()) {
			return null;
		}
		LogEntity logEntity = logEntities.get(rowIndex);
		switch (columnIndex) {
		case 0:
			return logEntity.getLogName();
		case 1:
			return logEntity.getLevel();
		case 2:
			return logEntity.getDate();
		case 3:
			return logEntity.getMesssage();
		case 4:
			return logEntity.getThrowable();
		default:
			return null;
		}
	}

	@Override
	public Class<?> getColumnClass(int columnIndex) {
		switch (columnIndex) {
		case 0:
			return String.class;
		case 1:
			return LogLevel.class;
		case 2:
			return Date.class;
		case 3:
			return String.class;
		case 4:
			return Throwable.class;
		default:
			return null;
		}
	}
}
